//
// Created by 抑~风 on 2022/12/9.
//

#ifndef CWJ_COMPILER_SEMANTIC_ANALYSIS_H
#define CWJ_COMPILER_SEMANTIC_ANALYSIS_H

#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <unordered_set>
#include <stack>
#include <queue>
#include"../regexp.h"

using namespace std;

namespace CWJ {

    namespace SEMA_ANALYSIS {


        class CwjAny {
        public:

            CwjAny() = default;

            CwjAny(const CwjAny& c){
                this->data_ = c.data_;
            }

            template<typename T>
            CwjAny(T data) :data_(std::make_shared<Derive < T>>
            (data)),isValid(true){};

            template<typename T>
            void setData(T);

            template<typename T>
            T cast_() const;

            CwjAny& operator=(const CwjAny &D )
            {
                this->data_ = D.data_;
                return *this;
            }




        private:
            class Base;

            template<typename T>
            class Derive;
            bool isValid = false;
            // 存储数据
            std::shared_ptr<Base> data_;
        };

        // CwjAng中的私有类
        class CwjAny::Base {
        public:
            virtual ~Base() = default;
        };

        // CwjAng中的私有类
        template<typename T>
        class CwjAny::Derive : public CwjAny::Base {
        public:
            explicit Derive(T d) : data_(d) {};

            T getData() { return data_; };
        private:
            T data_;
        };


        class Scope;

        enum class SymbolEnum {
            TYPE,
            VARIABLE,
            LITERAL,
            FUNCTIONOBJ,
            CLASSOBJ,
            BREAK,
            CONTINUE,
        };

        class Symbol {
        public:
            using ResASTNodePtr = CWJ::ResASTNode::ResASTNodePtr;
            using ResASTNode = CWJ::ResASTNode;

            static Symbol BREAK_SYMBOL;
            static Symbol CONTINUE_SYMBOL;

            Symbol(const SymbolEnum symbolEnum, const string &name, Scope *enclosingScope, const ResASTNodePtr ctx);

            const string &getName() const;

            Scope *getEnclosingScope() const;

            const ResASTNode *getCtx() const;

            void setName(const string &name);

            void setEnclosingScope(Scope *enclosingScope);

            void setCtx(ResASTNodePtr ctx);

            const SymbolEnum getSymbolEnum() const;


        protected:
            const SymbolEnum symbolEnum;
            string name;
            Scope *enclosingScope;
            ResASTNodePtr ctx;
        };

        enum class ScopeType{
            BLOCK,
            FUNCTION,
            CLASS,
        };

        class Scope {
        public:
            using ResASTNodePtr = ResASTNode::ResASTNodePtr;

            void addSymbol(Symbol *symbol);

            void addSubScope(Scope *scope);

            void setParentScope(Scope *parentScope);

            Scope *getParentScope() const;

            const vector<Symbol *> &getSymbols() const;

            Scope(const ResASTNodePtr ctx = nullptr, Scope *parentScope = nullptr);

            ScopeType getType() const;

            const vector<Scope *> &getSubScopes() const;


        protected:
            ScopeType type = ScopeType::BLOCK;
            Scope *parentScope = nullptr;
            vector<Symbol *> symbols;
            vector<Scope *> subScopes;
            ResASTNodePtr ctx = nullptr;
        };





        enum class TypeEnum {
            TYPE,
            FUNCTION,
            CLASS,
            VARIABLE,
            VOID,
            INT,
            FLOAT,
            STRING,
        };
        class Variable;
        class CWJType : public Symbol {
        public:
            using ResASTNodePtr = ResASTNode::ResASTNodePtr;

            CWJType(const string &name, Scope *enclosingScope, const ResASTNodePtr ctx,
                    const TypeEnum uid, bool isPrimary = false);

            CWJType(const CWJType&) = default;

            bool isType(CWJType type);

            bool isPrimaryType();

            const TypeEnum getUid() const;

            bool isPrimary1() const;

            static CWJType voidType;
            static CWJType intType;
            static CWJType floatType;
            static CWJType stringType;

        protected:
            const TypeEnum uid;
            bool isPrimary = false;
        };

        class FunctionType : public CWJType {
        public:

            static FunctionType PRINT;
            static FunctionType PRINTLN;

            FunctionType(const string &name, Scope *scope, const ResASTNodePtr ctx);

            FunctionType(Scope *scope, const ResASTNodePtr ctx);

            void addParamType(CWJType *t);

            void addParam(Variable*);

            const CwjAny &getObject() const;

            void setObject(const CwjAny &object);

            const ResASTNodePtr getNode() const;

            void setNode(const ResASTNodePtr node);

            CWJType *getReturnTye() const;

            void setReturnTye(CWJType *returnTye);

            bool match(const vector<CWJType*>&);

            const vector<CWJType *> &getParamTypes() const;

            const vector<Variable *> &getParamsVars() const;

            ResASTNodePtr getBody() const;

            void setBody(ResASTNodePtr body);

        protected:
            CwjAny object;
            CWJType *returnTye;
            ResASTNodePtr  body;
            vector<CWJType *> paramTypes;
            vector<Variable*> paramsVars;
        };

        class ClassType : public CWJType {
        public:
            ClassType(const string &name, Scope *scope, const ResASTNodePtr node);

            ClassType(Scope *scope, const ResASTNodePtr node);

        protected:
            CwjAny object;
        };

        class Variable:public Symbol{
        public:
            Variable(const string &name, Scope *enclosingScope, const ResASTNodePtr ctx,
                     CWJType *type, const CwjAny &val,bool isValid = false);

            CWJType *const getType() const;


            const CwjAny &getVal() const;

            void setVal(const CwjAny &val);

            void setType(CWJType *type);

            bool isValid1() const;

            void setIsValid(bool isValid);

            void assgin(Variable* var);

            friend ostream &operator<<(ostream &os, const Variable &variable);

        private:
            CWJType* type;
            CwjAny val;
            bool isValid{false};
        };

        class Literal:public Symbol{
        public:
            Literal(Scope *enclosingScope, const ResASTNodePtr ctx, CWJType *type, const string &val);
        private:
            static int uid;
            const static string prefix;
            const string val;
            CWJType* type;
        };

        class FunctionObj: public Symbol{
        public:
            FunctionObj(const SymbolEnum symbolEnum, const string &name, Scope *enclosingScope, const ResASTNode *ctx,
                        const FunctionType &type);

        private:
            FunctionType type;
            vector<Variable*>params;
        };


        class FunctionScope: public Scope{
        public:
            FunctionScope(const ResASTNodePtr ctx = nullptr, Scope *parentScope = nullptr,FunctionType * obj = nullptr);

            FunctionType *getObject() const;

        private:
            FunctionType* object;
        };

        class ClassScope: public Scope{
        public:
            ClassScope(const ResASTNodePtr ctx = nullptr, Scope *parentScope = nullptr,ClassType * obj = nullptr);

            ClassType *getObject() const;

        private:
            ClassType* object;
        };



        class AnnotatedTree {
        public:
            using ResASTNode = CWJ::ResASTNode;
            using ResASTNodePtr = CWJ::ResASTNode::ResASTNodePtr;
            using NodeType = ResASTNode::NodeType;

            Symbol *getSymbolOfNode(ResASTNodePtr ptr);

            void addSymbolOfNode(ResASTNodePtr, Symbol *);

            Scope *getScopeOfNode(ResASTNodePtr ptr);

            void addScopeOfNode(ResASTNodePtr, Scope *);

            void setTree(const ResASTNodePtr tree);

            const ResASTNodePtr getTree() const;

            friend ostream &operator<<(ostream &os, const AnnotatedTree &tree);

            CWJType* lookupClass(ResASTNodePtr ctx,string name);

            Variable* lookupVar(ResASTNodePtr ctx,string name);

            FunctionType* lookupFunction(ResASTNodePtr ctx,const string& name,const vector<CWJType*>&list);

            void showScopeTree();

            void showScopeTree(Scope*,string);

            CWJType* getType(ResASTNodePtr);
            void  getIdent(ResASTNodePtr node,string& str);

        private:
            ResASTNodePtr tree;
            unordered_map<ResASTNodePtr, Symbol *> symbolOfNodes;
            unordered_map<ResASTNodePtr, Scope *> scopeOfNodes;
        };

        class Scanner {
        public:

            using AnnotatedTree = CWJ::SEMA_ANALYSIS::AnnotatedTree;
            using AnnotatedTreePtr = AnnotatedTree *;
            using ResASTNode = AnnotatedTree::ResASTNode;
            using ResASTNodePtr = AnnotatedTree::ResASTNodePtr;

            virtual AnnotatedTreePtr visit(AnnotatedTreePtr) = 0;

            using NodeType = ResASTNode::NodeType;

        protected:
            AnnotatedTreePtr at{nullptr};
            stack<Scope *> sta;
        };


        class TypeAndScopeScanner : public Scanner {
        public:
            AnnotatedTreePtr visit(AnnotatedTreePtr tree);

        private:
            Scope *getCurScope();

            void pushScope(Scope *scope);

            void visit(ResASTNodePtr const root);
        };

        class TypeResolver : public Scanner{
        public:
            AnnotatedTreePtr visit(AnnotatedTreePtr tree);
        private:
            void visit(ResASTNodePtr tree);
            CWJType* getType(ResASTNodePtr);
            void  getIdent(Scanner::ResASTNodePtr node,string& str);

            void visitVarDector(Scope *scope, const CWJType *type, ResASTNodePtr const &one);

            void visitVarDectors(ResASTNode *node);
        };

        class RefResolver : public Scanner {
        public:
            AnnotatedTreePtr visit(AnnotatedTreePtr tree);
        private:
            void visit(ResASTNodePtr tree);
            void visitExpression(ResASTNodePtr node);
        };


        class ASTExecutor{
        public:
            using AnnotatedTree = CWJ::SEMA_ANALYSIS::AnnotatedTree;
            using AnnotatedTreePtr = AnnotatedTree *;
            using ResASTNode = AnnotatedTree::ResASTNode;
            using ResASTNodePtr = AnnotatedTree::ResASTNodePtr;

            void execute(AnnotatedTreePtr tree);

        private:

            void execute(ResASTNodePtr ctx);

            AnnotatedTreePtr at;


            Symbol* executeStatement(ResASTNodePtr ctx);

            Symbol* executeBlock(ResASTNodePtr ctx);

            void executeVarDectors(ResASTNodePtr ctx);

            Variable* executeExpression(ResASTNodePtr ctx);

            Variable* executeVarDector(ResASTNodePtr ctx);

            Variable *executVariableInitializer(ResASTNodePtr ctx);

            Variable *executeAndEpression(ResASTNodePtr ctx);

            void executeAndExpression(ResASTNodePtr ctx, Variable &res,int& flag);

            Variable *execRelaExpress(ResASTNodePtr ctx);

            void execexecRelaExpress(ResASTNodePtr ctx, Variable &res, int &flag);

            Variable *execAddExpress(ResASTNodePtr ctx);

            void execAddExpress(ResASTNodePtr ctx, Variable &res, int &flag);

            Variable *execDopExpress(ResASTNodePtr ctx);

            void execDopExpress(ResASTNodePtr ctx, Variable &res, int &flag);

            Variable *execMulExpress(ResASTNodePtr ctx);

            void execMulExpress(ResASTNodePtr ctx, Variable &res, int &flag);

            Variable *execPrimary(ResASTNodePtr ctx);

            void execAssginExpress(ResASTNodePtr ctx);

            Symbol *execIfStatement(ResASTNodePtr ctx,bool& flag,bool&isEnd);

            Symbol *execIfStatement(ResASTNodePtr ctx);

            bool execParExpress(ResASTNodePtr ctx);

            Symbol* execWhileStatement(ResASTNodePtr ctx);

            Symbol *execDoWhileState(ResASTNodePtr ctx);

            Symbol *execForStatement(ResASTNodePtr ctx);

            void findSubForControl(ResASTNodePtr ctx, vector<ResASTNodePtr> &list);

            void execForinit(ResASTNodePtr ctx);

            void execExpressList(ResASTNodePtr ctx);

            Variable* execFunctionCall(ResASTNodePtr ctx);

            void  calcuExpressionList(ResASTNodePtr ctx, vector<Variable *> &params);

            Variable *execFunctionCall(FunctionType *ctx, const vector<Variable *> &params);
        };


        class CWJCompiler {
        public:
            using ResASTNode = CWJ::ResASTNode;
            using ResASTNodePtr = CWJ::ResASTNode::ResASTNodePtr;
            using AnnotatedTree = CWJ::SEMA_ANALYSIS::AnnotatedTree;
            using AnnotatedTreePtr = AnnotatedTree *;

            AnnotatedTreePtr compile(string script);

            void execute(AnnotatedTreePtr);

        private:
            AnnotatedTreePtr compile(ResASTNodePtr root);

            GrammarParser parser;
            GrammarLever lever{"CommonLexer.txt"};
            AnnotatedTreePtr rootNode;
        };


        // CwjAny 类
        template<typename T>
        void CwjAny::setData(T data) {
            this->data_ = std::make_shared<CwjAny::Derive<T>>(data);
            this->isValid = true;
        };

        template<typename T>
        T CwjAny::cast_() const  {
            if(!data_ )  throw runtime_error("沒有内容无法转化");
            auto one = dynamic_cast<Derive<T> *>(data_.get());
            if (one == nullptr) {
                throw std::runtime_error("CwjAny中存储的类型和要转化的类型不匹配");
            }
            return one->getData();
        }


    }
}


#endif //CWJ_COMPILER_SEMANTIC_ANALYSIS_H
